package com.ly.system.service.exam.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.ly.common.core.constants.Constants;
import com.ly.common.core.enums.ResultCode;
import com.ly.common.security.exception.ServiceException;
import com.ly.system.domain.exam.Exam;
import com.ly.system.domain.exam.ExamQuestion;
import com.ly.system.domain.exam.dto.ExamAddDTO;
import com.ly.system.domain.exam.dto.ExamEditDTO;
import com.ly.system.domain.exam.dto.ExamQueryDTO;
import com.ly.system.domain.exam.dto.ExamQuestionAddDTO;
import com.ly.system.domain.exam.vo.ExamDetialVO;
import com.ly.system.domain.exam.vo.ExamVO;
import com.ly.system.domain.question.Question;
import com.ly.system.domain.question.vo.QuestionVO;
import com.ly.system.manager.ExamCacheManager;
import com.ly.system.mapper.exam.ExamMapper;
import com.ly.system.mapper.exam.ExamQuestionMapper;
import com.ly.system.mapper.question.QuestionMapper;
import com.ly.system.service.exam.IExamService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
//ServiceImpl<ExamQuestionMapper,ExamQuestion> 批量操作的Mapper,操作的实体的范型,操作哪个传哪个
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private ExamQuestionMapper examQuestionMapper;
    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examDTO) {
        PageHelper.startPage(examDTO.getPageNum(), examDTO.getPageSize());
        return examMapper.selectExamList(examDTO);
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        checkExamSaveParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtils.copyProperties(examAddDTO, exam);
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {

        //判断竞赛是否存在
        Exam exam = getExam(examQuestionAddDTO.getExamId());

        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }

        // 2. 从DTO中获取题目ID集合
        Set<Long> questionIdSet = examQuestionAddDTO.getQuestionIdSet();

        // 3. 如果题目ID集合为空，直接返回true
        if (CollectionUtil.isEmpty(questionIdSet)) {
            return true;
        }

        // 4. 根据ID集合批量查询题目
        List<Question> questionList = questionMapper.selectBatchIds(questionIdSet);

        // 4. 根据ID集合批量查询题目
        if (CollectionUtil.isEmpty(questionList) || questionList.size() < questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
        }

        return saveExamQuestion(exam, questionIdSet);

    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        Exam exam = getExam(examId);
        //10:00 before 11:00
        checkExam(exam);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId)
        );
    }

    @Override
    public ExamDetialVO detail(Long examId) {

        ExamDetialVO examDetialVO=new ExamDetialVO();
        Exam exam = getExam(examId);
        BeanUtils.copyProperties(exam,examDetialVO);
        //LambdaQueryWrapper<ExamQuestion>创建查询
        //ExamQuestion::getExamId是否与examId相等
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(
                new LambdaQueryWrapper<ExamQuestion>()
                        .select(ExamQuestion::getQuestionId)
                        .eq(ExamQuestion::getExamId, examId)
                        .orderByAsc(ExamQuestion::getQuestionOrder));
        //如果examQuestionList等于空,则说明里面没有题目
        if(CollectionUtil.isEmpty(examQuestionList)){
            //返回详情 只包含竞赛的基本信息
            return examDetialVO;
        }

        //select * from tb_question_id in (1,2,3)从 tb_question 表中查询 id 值为 1、2 或 3 的所有记录。
        // 获取所有试题ID后查询详情
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();

        //根据试题id查询前端所展示出的内容
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId, Question::getTitle, Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));

        //返回给前端的数据
        //        List<QuestionVO> questionVOList=new ArrayList<>();
        List<QuestionVO> questionVOList= BeanUtil.copyToList(questionList, QuestionVO.class);
        //根据题目ID查询出题目内容，并将处理后的题目信息设置到考试详情对象中，最终返回给前端。
        examDetialVO.setExamQuestionList(questionVOList);
        return examDetialVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        //竞赛标题是否重复进行判断
        //竞赛开始,结束时间进行判断
        checkExamSaveParams(examEditDTO,examEditDTO.getExamId());
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        if(Constants.TRUE.equals(exam.getStatus())){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(exam);
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        extracted(examId);
        exam.setStatus(Constants.TRUE);

        //要将新发布的竞赛数据存储到redis
        // e:t:l未完赛列表  e:h:l已完赛列表  e:d:examId
        examCacheManager.addCache(exam);
        return examMapper.updateById(exam);

    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if(exam.getEndTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_IS_FINISH);
        }
        exam.setStatus(Constants.FALSE);
        examCacheManager.deleteCache(examId);
        return examMapper.updateById(exam);
    }

    private void extracted(Long examId) {
        // select count(0) from tb_exam_question where exam_id=#{examId}
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if(count==null||count<=0){
            throw new ServiceException(ResultCode.EXAM_NOT_HAS_QUESTION);
        }
    }

    private void checkExamSaveParams(ExamAddDTO examSaveDTO,Long examId) {

        List<Exam> examList = examMapper.selectList(
                new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examSaveDTO.getTitle())
                        .ne(examId!=null,Exam::getExamId,examId)
        );

        if (CollectionUtil.isNotEmpty(examList)) {
            throw new ServiceException(ResultCode.EXAM_RESULT_REPEAT);
        }


        if (examSaveDTO.getStartTime() == null || examSaveDTO.getEndTime() == null) {
            throw new ServiceException(ResultCode.EXAM_START_EXAM_TIME_NOT_EXIST);
        }


        if (examSaveDTO.getStartTime().isBefore((LocalDateTime.now()))) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);//竞赛开始时间不能早于当前时间
        }

        if (examSaveDTO.getStartTime().isAfter(examSaveDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);//竞赛开始时间不能早于竞赛结束时间
        }

    }

    private void checkExam(Exam exam){
        if(exam.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }


    private boolean saveExamQuestion(Exam exam, Set<Long> questionIdSet) {
        // 初始化题目序号
        int num = 1;

        // 创建考试题目关联列表
        List<ExamQuestion> examQuestionList = new ArrayList<>();

        // 遍历题目ID集合
        for (Long questionId : questionIdSet) {
            ExamQuestion examQuestion = new ExamQuestion();
            // 设置考试ID
            examQuestion.setExamId(exam.getExamId());
            // 设置题目ID
            examQuestion.setQuestionId(questionId);
            // 设置题目顺序(自动递增)
            examQuestion.setQuestionOrder(num++);
            // 添加到列表
            examQuestionList.add(examQuestion);
        }
        //批量操作的方式
        return saveBatch(examQuestionList);
    }

    private Exam getExam(Long examId) {
        // 根据考试ID查询考试信息
        Exam exam = examMapper.selectById(examId);

        // 如果考试不存在则抛出异常
        if (exam == null) {
            throw new ServiceException(ResultCode.EXAM_RESULT_NOT_EXIST);
        }

        return exam;
    }

}